﻿#if (OLD)

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using nRoute.Components.Collections;
using nRoute.Components.Composition.Adapters;
using nRoute.Components.Mapping;
using nRoute.Components.Messaging;
using nRoute.Utilities;
#if (WINRT)
using System.Threading.Tasks;
#endif

namespace nRoute.Components.Composition
{
    public sealed partial class ResourceCatalog
        : IServiceProvider, IDisposable, IResourceCatalog, IResourceCatalogEx
    {

#region Declarations

        private const string CANNOT_RESOLVE_RESOURCE = "Cannot resolve resource of type {0}";
        private const string CANNOT_RESOLVE_LOCATOR = "Cannot resolve locator for type {0}";
        private const int INITIAL_CAPACITY = 251;
        private const int CONCCURRENCY_LEVEL_FACTOR = 2;

        private readonly CompositeDisposable _compositeDisposable;
        private readonly ConcurrentDictionary<Type, ResourceLocatorCatalog> _resourceLocators;
        private readonly ConcurrentOrderedList<KeyValuePair<Func<Type, bool>, Func<Type, ILocatorAdapter>>> _resourceAdapters;
        private readonly ITypeBuilder _typeBuilder;

#endregion

        public ResourceCatalog()
        {
            _compositeDisposable = new CompositeDisposable(INITIAL_CAPACITY);
            _resourceLocators = new ConcurrentDictionary<Type, ResourceLocatorCatalog>(Compatability.PROC_COUNT * CONCCURRENCY_LEVEL_FACTOR, INITIAL_CAPACITY);
            _resourceAdapters = new ConcurrentOrderedList<KeyValuePair<Func<Type, bool>, Func<Type, ILocatorAdapter>>>();
            _typeBuilder = new TypeBuilder(this);
        }

#region IServiceProvider Related

        object IServiceProvider.GetService(Type serviceType)
        {
            return GetResource(serviceType);
        }

#endregion

#region IResourceCatalog Related

        public object GetResource(Type resourceType)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");
            return _resourceLocators[resourceType].ResolveDefaultResourceLocator(true).GetResource(_typeBuilder);
        }

        public T GetResource<T>()
        {
            return (T)GetResource(typeof(T));
        }

        public object GetResource(Type resourceType, string key)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");
            Guard.ArgumentNotNullOrWhiteSpace(key, "key");

            return _resourceLocators[resourceType][key].GetResource(_typeBuilder);
        }

        public T GetResource<T>(string key)
        {
            return (T)GetResource(typeof(T), key);
        }

        public bool IsResourceable(Type resourceType)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");

            var _catalog = default(ResourceLocatorCatalog);
            if (_resourceLocators.TryGetValue(resourceType, out _catalog))
            {
                return !_catalog.IsEmpty;
            }
            return false;
        }

        public bool IsResourceable<T>()
        {
            return IsResourceable(typeof(T));
        }

        public bool IsResourceable(Type resourceType, string key)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");
            Guard.ArgumentNotNullOrWhiteSpace(key, "key");

            var _catalog = default(ResourceLocatorCatalog);
            if (_resourceLocators.TryGetValue(resourceType, out _catalog))
            {
                return _catalog.ContainsKey(key);
            }
            return false;
        }

        public bool IsResourceable<T>(string key)
        {
            return IsResourceable(typeof(T), key);
        }

        public bool TryGetResource(Type resourceType, out object resourceInstance)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");
            return TryGetResource<Object>(resourceType, out resourceInstance);
        }

        public bool TryGetResource<T>(out T resourceInstance)
        {
            return TryGetResource<T>(typeof(T), out resourceInstance);
        }

        public bool TryGetResource(Type resourceType, string key, out object resourceInstance)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");
            Guard.ArgumentNotNullOrWhiteSpace(key, "key");

            return TryGetResource<Object>(resourceType, key, out resourceInstance);
        }

        public bool TryGetResource<T>(string key, out T resourceInstance)
        {
            Guard.ArgumentNotNullOrWhiteSpace(key, "key");
            return TryGetResource<T>(typeof(T), key, out resourceInstance);
        }

        public IEnumerable<Object> GetAllResources(Type resourceType)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");
            return GetAllResources<Object>(resourceType);
        }

        public IEnumerable<T> GetAllResources<T>()
        {
            return GetAllResources<T>(typeof(T));
        }

#endregion

#region IResourceCatalogEx Related

        public IResourceLocator GetResourceLocator(Type resourceType)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");
            return _resourceLocators[resourceType].ResolveDefaultResourceLocator(true);
        }

        public IResourceLocator GetResourceLocator<T>()
        {
            return GetResourceLocator(typeof(T));
        }

        public IResourceLocator GetResourceLocator(Type resourceType, string key)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");
            Guard.ArgumentNotNullOrWhiteSpace(key, "key");

            return _resourceLocators[resourceType].ResolveResourceLocator(key, true);
        }

        public IResourceLocator GetResourceLocator<T>(string key)
        {
            return GetResourceLocator(typeof(T), key);
        }

        public IEnumerable<IResourceLocator> GetResourceCatalog(Type resourceType)
        {
            var _catalog = default(ResourceLocatorCatalog);
            if (_resourceLocators.TryGetValue(resourceType, out _catalog))
            {
                return _catalog.Values;
            }
            return Enumerable.Empty<IResourceLocator>();
        }

        public IEnumerable<IResourceLocator> GetResourceCatalog<T>()
        {
            return GetResourceCatalog(typeof(T));
        }

        public void CatalogAsKnownType(Type resourceType)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");
            GetOrAddCatalog(resourceType).RegisterAsKnownType();
        }

        public void CatalogAsKnownType<T>()
        {
            CatalogAsKnownType(typeof(T));
        }

        public bool CatalogResource(Type resourceType, IResourceLocator resourceLocator)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");
            Guard.ArgumentNotNull(resourceLocator, "resourceLocator");

            var _catalog = GetOrAddCatalog(resourceType);
            return _catalog.RegisterResource(resourceLocator);
        }

        public bool CatalogResource<T>(IResourceLocator resourceLocator)
        {
            return CatalogResource(typeof(T), resourceLocator);
        }

        public void CatalogDefaultResourceKey(Type resourceType, string key)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");

            _resourceLocators[resourceType].RegisterDefaultResourceLocator(key);
        }

        public void CatalogDefaultResourceKey<T>(string key)
        {
            CatalogDefaultResourceKey(typeof(T), key);
        }

        public bool UncatalogResource(Type resourceType, string key)
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");
            Guard.ArgumentNotNullOrWhiteSpace(key, "key");

            return _resourceLocators[resourceType].UnregisterResource(key);
        }

        public bool UncatalogResource<T>(string key)
        {
            return UncatalogResource(typeof(T), key);
        }

#endregion

#region Helpers

        private ResourceLocatorCatalog GetOrAddCatalog(Type resourceType)
        {
            return _resourceLocators.GetOrAdd(resourceType, (t) =>
            {
                var _newCatalog = new ResourceLocatorCatalog(t, this);
                _compositeDisposable.Add(_newCatalog);

                return _newCatalog;
            });
        }

        private IEnumerable<TResult> GetAllResources<TResult>(Type resourceType)
        {
            var _catalog = default(ResourceLocatorCatalog);
            if (_resourceLocators.TryGetValue(resourceType, out _catalog))
            {
                return _catalog.Select((k) => (TResult)k.Value.GetResource(this._typeBuilder));
            }
            return Enumerable.Empty<TResult>();
        }

        private bool TryGetResource<TResult>(Type resourceType, out TResult resourceInstance)
        {
            var _catalog = default(ResourceLocatorCatalog);
            if (_resourceLocators.TryGetValue(resourceType, out _catalog))
            {
                var _resourceLocator = _catalog.ResolveDefaultResourceLocator(false);
                if (_resourceLocator != null)
                {
                    resourceInstance = (TResult)_resourceLocator.GetResource(this._typeBuilder);
                    return true;
                }
            }
            resourceInstance = default(TResult);
            return false;
        }

        private bool TryGetResource<TResult>(Type resourceType, string key, out TResult resourceInstance)
        {
            var _catalog = default(ResourceLocatorCatalog);
            if (_resourceLocators.TryGetValue(resourceType, out _catalog))
            {
                var _resourceLocator = _catalog.ResolveResourceLocator(key, false);
                if (_resourceLocator != null)
                {
                    resourceInstance = (TResult)_resourceLocator.GetResource(this._typeBuilder);
                    return true;
                }
            }
            resourceInstance = default(TResult);
            return false;
        }

#endregion

#region Internal

        private void Dispose(bool disposing) 
        {
            if (disposing)
            {
                if (!_compositeDisposable.IsDisposed)
                {
                    _compositeDisposable.Dispose();
                    _resourceLocators.Clear();
                    
                    //_adapters.Clear(); <-- need to do this as well
                }
            }
        }

#endregion

#region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

#endregion

        ~ResourceCatalog() { Dispose(false); }

    }
}

#endif